package com.dixon.worddayday.service;

import com.dixon.worddayday.config.BusinessException;
import com.dixon.worddayday.dao.*;
import com.dixon.worddayday.dto.ReviewResult;
import com.dixon.worddayday.dto.TestResult;
import com.dixon.worddayday.model.*;
import com.dixon.worddayday.request.ReviewReq;
import com.dixon.worddayday.request.TestResultReq;
import com.dixon.worddayday.util.CurveFactory;
import com.dixon.worddayday.util.IForgettingCurve;
import com.dixon.worddayday.util.WordFormatUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.transaction.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author dixon
 * @version 1.0.0
 * @Description TODO
 * @createTime 2023年04月29日 21:45:00
 */
@Service
@Slf4j
public class WordsService {
    @Autowired
    private WordsRepository wordsRepository;
    @Autowired
    private ProgressRepository progressRepository;

    @Autowired
    private ReviewRecordRepository reviewRecordRepository;

    @Autowired
    private WordsTestRepository wordsTestRepository;

    @Autowired
    private WordsTestDetailRepository wordsTestDetailRepository;

    @Autowired
    private WordCrawler wordCrawler;

    @Transactional
    public void addWord(Customer customer, String word, int confirm) {
        word = word.trim().toLowerCase();
        Words words = wordsRepository.findByWord(word);

        if(words == null) {
            words = wordCrawler.craw(word);
            Assert.notNull(words, "下载单词失败");

            words.setCreateTime(LocalDateTime.now());
            wordsRepository.save(words);
            log.info("{} words saved", word);
        }

        if(confirm == 0) {
            List<Progress> existProgress = progressRepository.findByWordIdAndCustomerId(words.getId(), customer.getId());
            if(!existProgress.isEmpty()) {
                throw new BusinessException("已存在单词", "c2001");
            }
        }

        Progress progress = Progress.builder()
                .wordId(words.getId())
                .customerId(customer.getId())
                .lastReviewTime(LocalDateTime.now())
                .nextReviewTime(LocalDateTime.now())
                .reviewTimes(0)
                .totalReviewTimes(0)
                .createTime(LocalDateTime.now())
                .status("0")
                .build();

        progressRepository.save(progress);
        log.info("progress saved");
    }

    public List<Progress> getReviewWords(Customer customer) {
        Map<String, Object> params = new HashMap<>();
        params.put("customerId", customer.getId());
        params.put("currentDate", LocalDate.now());
//        params.put("notEqualStatus", "2");
        params.put("inStatus", Arrays.asList("0", "1"));
        List<Progress> reviewWordList = progressRepository.findAll(new ProgressSpecification(params), Sort.by(Sort.Direction.ASC, "createTime"));

        replaceWord(reviewWordList);

        return reviewWordList;
    }

    /**
     * 忘记复习的单词
     * @param customer
     * @return
     */
    public List<Progress> getForgotWords(Customer customer) {
        Map<String, Object> params = new HashMap<>();
        params.put("customerId", customer.getId());
        params.put("forgotCurrentDate", LocalDate.now());
//        params.put("notEqualStatus", "2");
        params.put("inStatus", Arrays.asList("0", "1"));
        List<Progress> reviewWordList = progressRepository.findAll(new ProgressSpecification(params), Sort.by(Sort.Direction.ASC, "createTime"));

        replaceWord(reviewWordList);

        return reviewWordList;
    }

    private void replaceWord(List<Progress> list) {
        list.stream().forEach(e -> {
            if(e.getWords() != null && !StringUtils.isEmpty(e.getWords().getWord()) && !StringUtils.isEmpty(e.getWords().getWordShow())) {
                e.getWords().setWordShow(e.getWords().getWordShow().replaceAll("^"+e.getWords().getWord(), ""));// 只显示音标
            }
        });
    }

    /**
     * 复习单词
     * @param customer
     * @param reviewReq
     */
    @Transactional
    public ReviewResult review(Customer customer, ReviewReq reviewReq) {
        ReviewResult result = new ReviewResult();

        progressRepository.findById(reviewReq.getProgressId()).ifPresent(e -> {

            Optional<Words> wordOptional = wordsRepository.findById(e.getWordId());
            Assert.isTrue(wordOptional.isPresent(), "找不到单词");

            Words words = wordOptional.get();

            Integer reviewTimes = e.getReviewTimes();

            if(words.getWord().equals(reviewReq.getReviewWord().trim().toLowerCase())) {
                reviewTimes++;
                result.setSuccess(true);
            }
            else {
                reviewTimes--; // 记错则减1
                result.setSuccess(false);
            }

            reviewTimes = Math.max(0, reviewTimes);

            IForgettingCurve curve = CurveFactory.getCurve(e.getIntervals());
            int nextDay = curve.getNextDay(reviewTimes);

            // modify
            e.setLastReviewTime(LocalDateTime.now());
            e.setNextReviewTime(e.getLastReviewTime().plusDays(nextDay));
            e.setReviewTimes(reviewTimes);
            e.setTotalReviewTimes(curve.getTotalReviewTimes());
            e.setStatus(curve.isDone(reviewTimes) ? "2" : "1");

            Words returnWord = new Words();
            BeanUtils.copyProperties(words, returnWord);
            WordFormatUtil.formatWordExampleSentence(returnWord);
            result.setWords(returnWord);
            result.setNextReviewTime(e.getNextReviewTime().format(DateTimeFormatter.ofPattern("MM月dd日")));
            result.setProgress(e);

            progressRepository.save(e);
            log.info("saved progress");

            ReviewRecord reviewRecord = ReviewRecord.builder()
                    .customerId(customer.getId())
                    .progressId(e.getId())
                    .reviewResult(result.isSuccess() ? "1" : "2")
                    .reviewTimes(reviewTimes)
                    .createTime(LocalDateTime.now())
                    .build();

            reviewRecordRepository.save(reviewRecord);
            log.info("saved review record");
        });

        return result;
    }


    public void recordTest(Customer customer, TestResultReq req) {

        List<TestResult> testResultList = req.getTestResultList();
        int correctCount = testResultList.stream().filter(e -> e.getWord().equals(e.getInput())).collect(Collectors.toList()).size();

        WordsTest wordsTest = WordsTest.builder()
                .customerId(customer.getId())
                .testCount(testResultList.size())
                .correctCount(correctCount)
                .createTime(LocalDateTime.now())
                .build();

        wordsTestRepository.save(wordsTest);
        log.info("save word test ");

        List<WordsTestDetail> detailList = new ArrayList<>(testResultList.size());
        testResultList.stream().forEach(e -> {
            detailList.add(WordsTestDetail.builder()
                            .testId(wordsTest.getId())
                            .word(e.getWord())
                            .input(e.getInput())
                    .build());
        });

        wordsTestDetailRepository.saveAll(detailList);
        log.info("save word test details");
    }

}
